package com.cnoja.jmsncn.command;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.Map;

import org.apache.log4j.Logger;

import com.cnoja.jmsncn.inf.ICommand;
import com.cnoja.jmsncn.inf.ICommander;
import com.cnoja.jmsncn.inf.IResponse;
import com.cnoja.jmsncn.kernel.exceptions.CommandException;
import com.cnoja.jmsncn.kernel.exceptions.CommandValidateException;
import com.cnoja.jmsncn.utils.command.CommandUtils;

public class SocketReponseReceiver implements Runnable {

	private Socket socket = null;

	private Map<Long, ICommand> commandMap = null;

	private ICommander commander = null;

	public SocketReponseReceiver(Socket socket, Map<Long, ICommand> commandMap,
			ICommander commander) {
		this.socket = socket;
		this.commandMap = commandMap;
		this.commander = commander;
	}

	private static Logger logger = Logger
			.getLogger(SocketReponseReceiver.class);

	private volatile boolean run = true;

	public boolean isRun() {
		return run;
	}

	public void setRun(boolean run) {
		this.run = run;
	}

	public void run() {
		ByteArrayOutputStream output = new ByteArrayOutputStream();
		try {
			InputStream stream = socket.getInputStream();
			byte b = -1;
			boolean returnDetected = false;

			while (run && (b = (byte) stream.read()) != -1) {
				if (((char) b) == '\r') {
					returnDetected = true;
				} else if (returnDetected && ((char) b) == '\n') {

					output = processResponse(output);
				} else {
					returnDetected = false;
					output.write(b);
				}

			}

			

		} catch (IOException exception) {
			logger.error(exception);
			this.run = false;

		}
	}

	private ByteArrayOutputStream processResponse(ByteArrayOutputStream output)
			throws UnsupportedEncodingException {
		String commandString = output.toString("utf-8");
		logger.info("A new Response has received:" + commandString);
		IResponse response = CommandUtils
				.extractResponse(commandString);
		output = new ByteArrayOutputStream();
		if (this.commandMap.get(response.getTransactionId()) != null) {
			ICommand command = this.commandMap.get(response
					.getTransactionId());
			if (command != null) {
				if (logger.isDebugEnabled()) {
					logger.info("Find command with transaction id:"
							+ command.getTransactionId());
				}
				command.setResponse(response);
				if (command.needValidate()) {
					try {
						command.validateResponse(response);
						if (command.hasNext()) {
							ICommand next = command.getNext();
							this.commandMap.put(next
									.getTransactionId(), next);
							try {
								this.commander.execute(next);
							} catch (CommandException e) {
								logger.error(e);
							}
						}

					} catch (CommandValidateException e) {
						logger.error(e);
					} finally {
						this.commandMap.remove(command
								.getTransactionId());

					}
				}
			}

		} else {
			logger.error("The response:"
					+ response.getResponseContent()
					+ " has no corresponding command transaction");
		}
		return output;
	}

}
